<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    // 实现数组响应式

    // 1. 替换数组原型中7个方法
    const originalProto = Array.prototype
    // 克隆体原数组原型
    const arrayProto = Object.create(originalProto)
    // 可修改数组的7个方法 , 'sort'
    const changeMethods = ['push', 'pop', 'shift', 'unshift', 'slice', 'splice', 'sort']
    //  2. 在克隆的原型上，覆盖那些能够修改数组的更新方法，让他们在修改数组同时，还可以通知更新
    changeMethods.forEach(method => {
      arrayProto[method] = function () {
        // 进行原始操作
        originalProto[method].apply(this, arguments)
        // 覆盖操作：增加更新通知
        console.log(`数组正在执行${method}方法`);
      }
    })



    // 对象响应化
    function defineReactive(obj, key, value) {
      Object.defineProperty(obj, key, {
        get() {
          console.log('获取' + key);
          return value
        },
        set(newVal) {
          if (newVal !== value) {
            // console.log(newVal);
            // console.log(JSON.stringify(obj[key]));
            console.log(`正在改变${key}值:从${obj[key]}变为${newVal}`)
            value = newVal
          }
        }
      })
    }

    function observer(obj) {
      // 不是对象或者为null，不做响应式，结束
      if (typeof obj !== 'object' || obj === null) return;
      // 如果是数组，修改其实例的原型
      if (Array.isArray(obj)) {
        // 3. 将得到的新的原型设置到数组实例原型上
        obj.__proto__ = arrayProto

        // 4. 对数组内的元素，同样进行响应化
        for (let i = 0; i < obj.length; i++) {
          // console.log(obj[i]);
          observer(obj[i])
        }

        // 如果是对象
      } else {
        Object.keys(obj).forEach(key => {
          console.log(obj, key, obj[key]);
          defineReactive(obj, key, obj[key])
        })
      }
    }

    obj = [{ a: 1 }, 2, 7, 5, 3]
    observer(obj)
    obj.push(4) // 数组正在执行push方法
    obj.pop() // 数组正在执行pop方法
    obj[0].a = 2
    // 获取a    
    // 正在改变a值:从1变为2
    obj.sort()  // 数组正在执行sort方法
    console.log(obj); // [ 2, 3, 5, 7, { a: [Getter/Setter] } ]

    console.log(obj[4].a);
// 获取a
// 2

  </script>
</body>

</html>